/*
 * Copyright (c) 2022-2023 cetcclout Co., Ltd All rights reserved.
 * Sensor Adapter is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package com.cetccloud.cimp.adapter.server.netty;


import com.cetccloud.cimp.adapter.datasource.sqldb.vo.ServerConfig;
import com.cetccloud.cimp.adapter.protocol.ProtocolExecutor;
import com.cetccloud.cimp.adapter.protocol.chain.DefaultDataChain;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.nio.Buffer;
import java.util.concurrent.TimeUnit;

/**
 * @author
 * @apiNote Netty数据接收服务
 * @date 2022/12/28 14:48
 */
public class NettyServer implements ProtocolExecutor {
    private static final Logger LOGGER = LogManager.getLogger(NettyServer.class);

    private DefaultDataChain dataChain;

    private EventLoopGroup bossGroup = new NioEventLoopGroup(4);
    private EventLoopGroup workerGroup = new NioEventLoopGroup(4);

    public NettyServer(DefaultDataChain dataChain) {
        this.dataChain = dataChain;
        System.out.println("NettyServer::"+ this);
    }

    /**
     * 获取信息处理操作集
     */
    @Override
    public DefaultDataChain getDefaultDataChain(){
        return  this.dataChain;
    }

    /**
     * 启动指定的TCP服务
     */
    @Override
    public void start(ServerConfig serverConfig) {
        try {
            this.bind(serverConfig.getPort());
        } catch (InterruptedException e) {
            LOGGER.error(e);
        }
    }

    /**
     * 停止当前的TCP服务
     */
    @Override
    public void quit() {
        if(null != this.bossGroup) {
            this.bossGroup.shutdownGracefully();
        }
        if(null != this.workerGroup) {
            this.workerGroup.shutdownGracefully();
        }
        LOGGER.info("***********************************************");
        LOGGER.info("Netty Server stop !!!");
        LOGGER.info("***********************************************");
    }

    /**
     * 绑定端口并创建TCP服务
     */
    public void bind(int port) throws InterruptedException {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 2048)
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.childHandler(new ChildChannelHandler(dataChain));
        //initChannel0(bootstrap,this.dataChain);

        ChannelFuture cf = bootstrap.bind(port).sync();
        LOGGER.info("***********************************************");
        LOGGER.info("Netty Server started at port:{}", port);
        LOGGER.info("***********************************************");
        cf.addListener((future) -> {
            if(future.isSuccess()) {
                LOGGER.info("netty server bind success.");
            } else {
                LOGGER.error("netty server bind fail.", future.cause());
            }
        });
    }

    /**
     * 添加解析数据链路实现
     */
    private static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

        //传入链路入口
        private final DefaultDataChain dataChain;

        public ChildChannelHandler(DefaultDataChain dataChain) {
            this.dataChain = dataChain;
        }

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast("logging",new LoggingHandler(LogLevel.INFO));
            // 加入自定义分割符号
            ByteBuf delimiter = Unpooled.copiedBuffer("\r\n".getBytes());
            //ch.pipeline().addFirst(new DelimiterBasedFrameDecoder(8192, delimiter));
//            ch.pipeline().addLast(new ProtocolDataDecoder());
//            ch.pipeline().addLast(new ProtocolDataEncoder());
//            ch.pipeline().addLast(new IdleStateHandler(ProtocolConfig.getHeartbeatInterval(), ProtocolConfig.getHeartbeatInterval(), 0, TimeUnit.SECONDS));
            ch.pipeline().addLast(new IdleStateHandler(150, 0, 0, TimeUnit.SECONDS));
            ch.pipeline().addLast(new IdleStateTrigger());
            ch.pipeline().addLast("nettyServerHandler", new NettyServerHandler(dataChain));
        }
    }

    /**
     * 数据发往设备
     */
	public void toDtu(Buffer buffer, Object context) {
		//todo 转发到设备端
		ChannelHandlerContext ctx = ((ChannelHandlerContext) context);
        if (ctx != null && ctx.channel().isActive()) {
            ByteBuf buf = Unpooled.wrappedBuffer((byte[]) buffer.array());
            ctx.channel().writeAndFlush(buf);
        }else {
        	LOGGER.error("没有数据转发到终端监控仪！！！");
        }
	}

 }
